package com.legendframework.core.dao.transaction;

import com.legendframework.core.dao.Return;
import com.legendframework.core.dao.support.ClassUtils;
import com.legendframework.core.dao.transaction.enums.TransactionStatus;
import com.legendframework.core.dao.transaction.pojo.IOTransaction;
import com.legendframework.core.dao.yaml.AbstractYamlDao;
import com.legendframework.core.dao.yaml.Cache;
import com.legendframework.core.dao.yaml.support.BaseYml;

import java.io.Serializable;
import java.util.*;

/**
 * yaml事务实现
 *
 * 一个线程会有多个事务，利用栈模式实现事务的进出栈
 */
public class YamlTransaction extends AbstractTransaction<Deque<IOTransaction<BaseYml<Object>>>> {

    private final TransactionLock lock = new TransactionReentrantLock();;

    public TransactionLock getLock() {
        return lock;
    }

    @Override
    public Deque<IOTransaction<BaseYml<Object>>> getThreadLocalData() {
        Deque<IOTransaction<BaseYml<Object>>> ioTransactions = super.threadLocal.get();
        if (ioTransactions == null) {
            super.threadLocal.set(ioTransactions = new LinkedList<>());
        }
        return ioTransactions;
    }

    @Override
    public void start() {
        IOTransaction<BaseYml<Object>> ioTransaction = new IOTransaction<>(UUID.randomUUID().toString().replace("-",""),TransactionStatus.BIGIN);
        //事务进栈
        getThreadLocalData().push(ioTransaction);
    }

    @Override
    public void commit() {
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = getThreadLocalData().peek();
        if (peek != null) {
            for (Map.Entry<String, Return<BaseYml<Object>, IOTransaction.ExecuteType>> entry : peek.getPersistentExecutionChain().entrySet()) {
                Return<BaseYml<Object>, IOTransaction.ExecuteType> baseYmlExecuteTypeReturn = entry.getValue();
                //修改同级文件的备份区
                BaseYml<Object> baseYml = baseYmlExecuteTypeReturn.getR1();
                //获取需要替换的备份
                Object item = baseYml.getTransactionBackupsPool().get(peek.getId());
                for (String key : baseYml.getTransactionBackupsPool().keySet()) {
                    baseYml.getTransactionBackupsPool().put(key,item);
                }
                //清除备份
                baseYml.getTransactionBackupsPool().remove(peek.getId());
                //设置事务状态
                peek.setStatus(TransactionStatus.END);
                //执行持久化
                switch (baseYmlExecuteTypeReturn.getR2()){
                    case UPDATE:
                        baseYml.save(item);
                        break;
                    case DELETE:
                        baseYml.delete();
                }
                AbstractYamlDao.getCaches().get(baseYml.getEntityType()).onCacheCommit();
            }

            //事务出栈
            getThreadLocalData().poll();
            //释放锁
            lock.unlock(peek.getId());
        }
    }

    @Override
    public void back() {
        //事务出栈
        IOTransaction<BaseYml<Object>> poll = getThreadLocalData().poll();
        if (poll != null) {
            //清除备份
            for (Map.Entry<String, Return<BaseYml<Object>, IOTransaction.ExecuteType>> entry : poll.getPersistentExecutionChain().entrySet()) {
                Return<BaseYml<Object>, IOTransaction.ExecuteType> baseYmlExecuteTypeReturn = entry.getValue();
                BaseYml<Object> baseYml = baseYmlExecuteTypeReturn.getR1();
                baseYml.getTransactionBackupsPool().remove(poll.getId());
                //回滚缓存
                AbstractYamlDao.getCaches().get(baseYml.getEntityType()).onCacheBack();
            }
            //释放锁
            lock.unlock(poll.getId());
        }
    }

    /**
     * 校验线程变量中的参数
     * @return
     */
    protected boolean check(){
        return true;
    }

    @Override
    public void onMount() {
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = getThreadLocalData().peek();
        if (peek != null && peek.getStatus() == TransactionStatus.BIGIN) {
            peek.setStatus(TransactionStatus.NOT);
        }
    }

    @Override
    public void onRecovery() {
        //获取当前事务
        IOTransaction<BaseYml<Object>> peek = getThreadLocalData().peek();
        if (peek != null && peek.getStatus() == TransactionStatus.NOT) {
            peek.setStatus(TransactionStatus.BIGIN);
        }
    }
}
